#!/usr/local/bin/python3
# -*- coding: utf-8 -*-
# http://python.org/dev/peps/pep-0263/
import os
import sys
import time
from datetime import datetime
import pytz
from PyQt5.QtCore import pyqtSignal, Qt, QTime
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QMainWindow, QMessageBox, QFileDialog, QApplication, QProgressDialog
from mainUI import Ui_MainWindow
from serialPort import SerialPortWindow
from utils.binHandlers import BinHandler
from windowUI.multiTouchWindow import MultiTouchWindow
from windowUI.daubTouchWindow import DaubTouchWindow
from windowUI.picturesPlayWindow import PicturesPlayWindow
from windowUI.tpDrawLineWindow import TpDrawLineWindow
from windowUI.rectTouchTestWindow import RectTouchTestWindow
from windowUI.view2DWindow import View2DWindow
from windowUI.view3DWindow import View3DWindow
from windowUI.OthersTestWindow import OthersTestWindow
from windowUI.miscWindow import HelpWindow
from windowUI.viewParamWindow import ViewParamWindow
from windowUI.touchKeyWindow import TouchKeyWindow
from windowUI.gyroWindow import GyroWindow

'''
快捷键 说明：
    默认操作：按下 Tab 键，使用上下左右可以切换控件，按下 空格 键点击控件
    菜单栏（menubar）：
        文件  Alt+F
        选项  Alt+O
        帮助  Alt+H
    工具栏（toolBar）：
        全   屏   F11
        Engineer    
        陀螺仪窗口   无（模式切换控制）
        串   口   无（界面按钮控制）
        保存打印    Ctrl+S
        清除打印    无（界面按钮控制）
    中间控制按钮：
        上一 个    Ctrl+Up
        上一 个    Ctrl+Down
        开   始   Ctrl+空格
        结   束   无（界面按钮控制）
    底部按钮：
        手动发送    Ctrl+Return（Enter）
'''


class MainWindow(QMainWindow, Ui_MainWindow):
    filePath = "('', 'BIN files(*.bin , *.hex , *.BIN , *.run)')"
    # 声明带一个字典类型参数的信号
    mainSignal = pyqtSignal(dict)  # 主界面的信号用来绑定子界面类的函数方法
    toView2DSignal = pyqtSignal(dict)  # 主界面的信号用来绑定子界面类的函数方法
    toTestDetailSignal = pyqtSignal(dict)  # 主界面的信号用来绑定子界面类的函数方法
    modeList = ['SendCmd', 'PatternTest', 'OthersTest', 'RectTouch', 'TPDrawLine', 'DaubTouch', 'MultiTouch',
                'PicturesPlay', 'PlayVideo', 'ViewParam', 'GyroTest', 'TouchKey']
    curMode = 'SendCmd'

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)
        # 引入公共程序
        from utils.common import commonProgram
        # 引入配置文件
        self.myConfig = commonProgram.EnableConfigHandler()
        print('MainWindow get config Version', self.myConfig.version)
        # 获取显示器分辨率
        self.desktop = QApplication.desktop()
        self.screenRect = self.desktop.screenGeometry()
        self.screen_height = self.screenRect.height()
        self.screen_width = self.screenRect.width()
        self.screen_ratio = self.desktop.devicePixelRatio()
        self.screen_resolution = str(self.screen_width * self.screen_ratio) + 'x' + str(
            self.screen_height * self.screen_ratio)
        print("Screen height {}".format(self.screen_height))
        print("Screen width {}".format(self.screen_width))
        print("Screen ratio {}".format(self.screen_ratio))
        # 修改分辨率
        self.myConfig.version = '0.0.2'
        self.myConfig.screenLength = self.screen_width
        self.myConfig.screenWidth = self.screen_height
        self.myConfig.screenRatio = self.screen_ratio
        self.myConfig.screenResolution = self.screen_resolution
        print('MainWindow get new config Version', self.myConfig.version)
        # 确定图片路径
        self.myConfig.version = '0.0.3'
        rootPath = self.myConfig.picturesRootPath
        for item in os.scandir(rootPath):
            if item.is_dir():
                if item.name == self.screen_resolution:
                    self.myConfig.testPicturesPath = rootPath + self.screen_resolution + '/'
        # 初始化测试项
        self.myConfig.passTestItems = set()
        print('MainWindow get new config Version', self.myConfig.version)
        # 引入可控串口信息窗口
        self.serialPortWin = SerialPortWindow()
        self.view2DWin = View2DWindow()
        self.myConfig.serialPortWin = self.serialPortWin
        # 将自己的信号和Form2的接受函数绑定
        self.mainSignal.connect(self.serialPortWin.RecvMainData)
        self.toView2DSignal.connect(self.view2DWin.RecvMainData)
        self.statusbar.showMessage('未检测到测试设备')
        self.connect_init()
        # self.statusbar.showMessage('检测到显示屏')
        # 将form2的信号_singal_2和主界面的getData_F2连接
        self.serialPortWin.serialPortSignal.connect(self.RecvSerialData)
        # time.sleep(2)
        self.actionserial.trigger()
        # self.setWindowOpacity(0.7)  # 设置透明度

    def connect_init(self):
        self.btnStart.clicked.connect(self.StartButtonFun)
        self.btnLast.clicked.connect(self.LastButtonFun)
        self.btnNext.clicked.connect(self.NextButtonFun)
        self.btnOver.clicked.connect(self.OverButtonFun)
        self.btnHandSend.clicked.connect(self.HandSendFun)
        self.btnFileSend.clicked.connect(self.FileSendFun)
        self.radioSendCmd.clicked.connect(lambda: self.ModeChanged(self.modeList[0]))
        self.radioPatternTest.clicked.connect(lambda: self.ModeChanged(self.modeList[1]))
        self.radioOthersTest.clicked.connect(lambda: self.ModeChanged(self.modeList[2]))
        self.radioRectTouch.clicked.connect(lambda: self.ModeChanged(self.modeList[3]))
        self.radioTPDrawLine.clicked.connect(lambda: self.ModeChanged(self.modeList[4]))
        self.radioDaubTouch.clicked.connect(lambda: self.ModeChanged(self.modeList[5]))
        self.radioMultiTouch.clicked.connect(lambda: self.ModeChanged(self.modeList[6]))
        self.radioPicturesPlay.clicked.connect(lambda: self.ModeChanged(self.modeList[7]))
        self.radioPlayVideo.clicked.connect(lambda: self.ModeChanged(self.modeList[8]))
        self.radioViewParam.clicked.connect(lambda: self.ModeChanged(self.modeList[9]))
        self.radioGyroTest.clicked.connect(lambda: self.ModeChanged(self.modeList[10]))
        self.radioTouchKey.clicked.connect(lambda: self.ModeChanged(self.modeList[11]))
        # menubar
        self.actionHelp.triggered.connect(self.OpenHelpWin)
        # toolbar action
        self.actionserial.triggered.connect(self.OpenSerialPortWindow)
        self.actionSaveLog.triggered.connect(self.SaveLogs)
        self.actionView3D.triggered.connect(self.OpenView3DWindow)
        self.actionFullScreen.triggered.connect(self.FullScreenShow)

    # # type: send-发往串口，statusbar-显示在statusbar(状态栏)，text,.., - 显示在 textBrowser, null-不需要显示
    def DataHandleFun(self, dataDict: dict):
        dataStr = dataDict.get('data', '')
        typeStr = dataDict.get('type', 'null').strip()
        if typeStr == 'send':
            dataDict['type'] = 0
            self.mainSignal.emit(dataDict)
        if typeStr == 'view2D':
            dataDict['type'] = 0
            self.toView2DSignal.emit(dataDict)
        if typeStr == 'text':
            pass
        if typeStr == 'test':
            self.toTestDetailSignal(dataDict)
        if typeStr == 'statusbar':
            self.statusbar.showMessage(dataStr)
        if (typeStr != 'null') and (typeStr != 'statusbar'):
            # "%Y-%m-%d %H:%M:%S.%f
            # 指定Tokyo时区当前时间
            timeStr = datetime.now(pytz.timezone('Asia/Shanghai')).strftime("%Y-%m-%d %H:%M:%S.%f")
            displayMsg = '<{0}>[{1}]: {2}'.format(typeStr, timeStr, dataStr)
            # displayMsg =dataStr# "<%s>[%s]: %s".(msgType, timeStr, dataStr)
            # print(displayMsg)
            self.textBrowser.insertPlainText(displayMsg + '\r\n')
            # 获取到text光标
            textCursor = self.textBrowser.textCursor()
            # 滚动到底部
            textCursor.movePosition(textCursor.End)
            # 设置光标到text中去
            self.textBrowser.setTextCursor(textCursor)

    def ModeChanged(self, mode):
        modeStr = mode.strip()
        self.curMode = modeStr
        if modeStr == self.modeList[0]:
            print('SendCmd')
        if modeStr == self.modeList[1]:
            self.OpenView2DWindow()
            # print('PatternTest')
        if modeStr == self.modeList[2]:
            self.othersTestWin = OthersTestWindow()
            self.othersTestWin.show()
            # print('OthersTest')
        if modeStr == self.modeList[3]:
            self.rectTouchTestWin = RectTouchTestWindow()
            self.rectTouchTestWin.show()
        if modeStr == self.modeList[4]:
            self.tpDrawLineWin = TpDrawLineWindow()
            self.tpDrawLineWin.show()
        if modeStr == self.modeList[5]:
            # print('DaubTouch')
            self.daubTouchWin = DaubTouchWindow()
            self.daubTouchWin.show()
        if modeStr == self.modeList[6]:
            print('MultiTouch')
            self.multiTouWin = MultiTouchWindow()
            self.multiTouWin.show()
        if modeStr == self.modeList[7]:
            self.picturesPlayWin = PicturesPlayWindow()
            self.picturesPlayWin.show()
        if modeStr == self.modeList[8]:
            print('PlayVideo')
        if modeStr == self.modeList[9]:
            # print('ViewParam')
            self.viewParamWin = ViewParamWindow()
            self.viewParamWin.show()
            # self.displayWin.showFullScreen()
        # print('ModeChanged', modeStr)
        if modeStr == self.modeList[10]:
            # print('ViewParam')
            self.gyroWin = GyroWindow()
            self.gyroWin.show()
        if modeStr == self.modeList[11]:
            # print('ViewParam')
            self.touchKeyWindow = TouchKeyWindow()
            self.touchKeyWindow.show()

    def StartButtonFun(self, event):
        data = 'start'
        emitData = {"data": data, "type": 'send', }
        # print('StartButtonFun', event)
        self.DataHandleFun(emitData)

    def LastButtonFun(self, checked):
        # modeInd = self.modeList.index(self.curMode)
        # if modeInd == 0:
        #     self.curMode = self.modeList[len(self.modeList) - 1]
        # else:
        #     self.curMode = self.modeList[modeInd - 1]
        # exec("self.radio{}.click()".format(self.curMode))
        data = 'last'
        emitData = {"data": data, "type": "send", }
        self.DataHandleFun(emitData)

    def NextButtonFun(self, checked):
        # modeInd = self.modeList.index(self.curMode)
        # if modeInd == len(self.modeList) - 1:
        #     self.curMode = self.modeList[0]
        # else:
        #     self.curMode = self.modeList[modeInd + 1]
        # exec("self.radio{}.click()".format(self.curMode))
        data = 'next'
        emitData = {"data": data, "type": "send", }
        self.DataHandleFun(emitData)

    def OverButtonFun(self, checked):
        data = 'over'
        emitData = {"data": data, "type": "send", }
        self.DataHandleFun(emitData)

    def OpenHelpWin(self):
        # print('OpenInstructionWin')
        self.helpWin = HelpWindow()
        self.helpWin.show()

    def FullScreenShow(self, checked):
        if checked:
            self.showFullScreen()
        else:
            self.showNormal()
            # self.setupUi(self)

    def OpenView2DWindow(self):
        self.view2DWin.show()

    def OpenView3DWindow(self, checked):
        self.view3DWin = View3DWindow()
        if checked:
            self.view3DWin.show()
        else:
            self.view3DWin.close()
        # print('OpenView3DWindow', checked)

    def OpenSerialPortWindow(self, triggered):
        # print(triggered)
        if triggered:
            self.serialPortWin.show()  # 通过pushbutton 展示子界面  如果只是做数据的传递，不打开第二个界面，可以 不调用self.form2.show(） 方法
            # 向form2发送信号，将self.num发了出去
            # 通过自己的信号向子界面传递数据。要想多传递几个值，就在emit(值1，值2） 对应到子界面get_data接受就是2个参数，即get_data(值1，值2）
            # self.mainSignal.emit({"data": 'msgs', "type": 0, })
        else:
            self.serialPortWin.close()

    def RecvSerialData(self, dictData: dict):
        # 用于接收Form2发过来的数据
        # dataStr = dictData.get("data", None)  # num2 就是子界面传递过来的数据
        self.DataHandleFun(dictData)

    def TouchModeToView2D(self, dataStr):
        # print('TouchModeToView2D', dataStr)
        import re
        pattern = re.compile(r'([-+])?\d+([.]\d+)?,([-+])?\d+([.]\d+)?')
        matchObj = pattern.search(dataStr)
        matchList = None
        if matchObj:
            # group([group1, …]) 方法用于获得一个或多个分组匹配的字符串，当要获得整个匹配的子串时，可直接使用group()或group(0)；
            # start([group])方法用于获取分组匹配的子串在整个字符串中的起始位置（子串第一个字符的索引），参数默认值为 0；
            # end([group])方法用于获取分组匹配的子串在整个字符串中的结束位置（子串最后一个字符的索引 + 1），参数默认值为 0；
            # span([group])方法返回(start(group), end(group))。
            # print(matchObj.group(0))
            matchList = matchObj.group(0).split(',')
            # print(matchList)
        if matchList:
            data = {
                "x": matchList[0],
                "y": matchList[1],
            }
            self.TouchPointList.append(data)
            dictData = {"data": data, "type": "view2D", }
            self.DataHandleFun(dictData)
        else:
            dictData = {"data": dataStr, "type": "send", }
            self.DataHandleFun(dictData)

    def HandSendFun(self):
        dataStr = self.editSendData.text().strip()
        if not len(dataStr.strip()) > 0:
            QMessageBox.warning(self, "空数据", "请输入内容！")
            return None
        # print('HandSendFun', dataStr)
        dictData = {"data": dataStr, "type": "send", }
        self.DataHandleFun(dictData)

    # 打开文件操作
    def FileSendFun(self):
        if not self.serialPortWin.ser.isOpen():
            QMessageBox.critical(self, "Port Error", "请先打开串口！")
            return
        self.filePath = QFileDialog.getOpenFileName(self, '选择文件', '',
                                                    'BIN files(*.bin , *.hex , *.BIN , *.run),Text(*.txt)')
        # print(self.filePath)
        # self.FilePath.setText(str(self.filePath[0]))
        if self.filePath[0]:
            self.statusbar.showMessage('文件已经打开：{}'.format(str(self.filePath[0])), 1000)
            self.FileDataSend(self.filePath[0])
        else:
            self.statusbar.showMessage('未打开任何文件', 1000)

    def FileDataSend(self, fileName):
        textFile = open(fileName, 'rb')  # 打开二进制文件
        # self.fileSize = os.path.getsize(self.fileName)  # 获得文件大小
        contentList = textFile.readlines()  # 输出内容列表
        for item in contentList:
            self.TouchModeToView2D(item.decode('utf-8').strip())
        textFile.close()
        self.statusbar.showMessage('文件内容发送完成')

    def LoopSendFile(self):
        binHandler = BinHandler(self.filePath[0])
        for i in range(binHandler.fileLines):
            # for i in range(5):
            content = binHandler.GetFrame()
            self.s3__send_text.setText(content[0].decode('UTF-8'))
            self.curFileLine.setText(str(content[1]))
            # self.ser.write(content[0])

            # hex发送
            input_s = content[0]
            send_list = []
            while input_s != b'':
                try:
                    num = int(input_s[0:2], 16)
                except ValueError:
                    QMessageBox.critical(self, 'wrong data', '请输入十六进制数据，以空格分开!')
                    return None
                input_s = input_s[2:].strip()
                send_list.append(num)
            input_s = bytes(send_list)
            num = self.ser.write(input_s)
            # print(num)
            time.sleep(0.01)

    def SaveLogs(self):
        try:
            StrText = self.textBrowser.toPlainText()
            textStr = str(StrText)
            # 前面是地址，后面是文件类型,得到输入地址的文件名和地址txt(*.txt*.xls);;image(*.png)不同类别
            # 对话框的父容器parent：一般选为调用对话框的窗体对象。
            # 对话框标题caption：弹出对话框的标题。
            # 文件过滤器filter：选择不同后缀的文件。
            # 初始文件过滤器initialFilter：设置初始的文件过滤器。
            # 对话框options：一个枚举类型。例如：QFileDialog.ShowDirsOnly只显示目录等。
            # 获取到的fileName和filtUsed：是两个元组类型数据。
            filepath, type = QFileDialog.getSaveFileName(self, "打印内容保存", '', filter='txt(*.txt)')
            file = open(filepath, 'w')
            # print(filepath, type)
            file.write(textStr)
            file.close()
        except Exception as e:
            pass

    def btnCancel_clicked(self):
        pass

    def btnProgressbar_clicked(self):
        labelText = "正在复制文本。。。"
        btnText = "取消"
        minValue = 0
        maxValue = 100
        progressBarDialog = QProgressDialog(labelText, btnText, minValue, maxValue, self)
        progressBarDialog.canceled.connect(self.btnCancel_clicked)
        progressBarDialog.setWindowTitle("复制文件")
        progressBarDialog.setWindowModality(Qt.WindowModal)
        progressBarDialog.setAutoReset(True)
        progressBarDialog.setAutoClose(True)
        counter = QTime()
        for i in range(minValue, maxValue):
            progressBarDialog.setValue(i)
            progressBarDialog.setLabelText("正在复制文件，第 %d 个" % i)
            counter.start()
            while counter.elapsed() < 200:
                None
            if progressBarDialog.wasCanceled():
                break

    def closeEvent(self, event):
        reply = QMessageBox.question(self, '提示', "是否要关闭所有窗口?", QMessageBox.Yes | QMessageBox.Close | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            event.accept()
            sys.exit(0)  # 退出程序
        if reply == QMessageBox.Close:
            event.accept()
            self.close()
        else:
            event.ignore()


# 程序入口
if __name__ == "__main__":
    ######使用下面的方式一定程度上可以解决界面模糊问题--解决电脑缩放比例问题
    # debain 上面没有这个 ：setHighDpiScaleFactorRoundingPolicy
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    # QGuiApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    # 在主函数入口之前加入上面的设置即可解决
    # 适应高DPI设备
    # QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    # 解决图片在不同分辨率显示模糊问题
    QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
    # QApplication.setAttribute(Qt.AA_Use96Dpi)
    app = QApplication(sys.argv)
    font = QFont("宋体")
    pointsize = font.pointSize()
    font.setPixelSize(int(pointsize * 90 / 72))
    app.setFont(font)
    # myWin = MyMainWindow()
    mainWindow = MainWindow()
    mainWindow.show()
    sys.exit(app.exec_())
